/**
 * 
 */
package ro.dta.idbi.patterns;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import ro.dta.idbi.interfaces.IExtentGenerator;
import ro.dta.idbi.interfaces.IntegrationOperation;
import ro.dta.idbi.model.AbstractPattern;
import ro.dta.idbi.model.Utils;
import uk.ac.ic.doc.automed.IntegrityException;
import uk.ac.ic.doc.automed.NotFoundException;
import uk.ac.ic.doc.automed.modelmanagement.modeldef.SQLModelDef;
import uk.ac.ic.doc.automed.reps.Schema;
import uk.ac.ic.doc.automed.reps.SchemaObject;

/**
 * <b>Column to table transformation pattern</b><br />
 * 
 * Example of use:<br />
 * 
 * <code>col_to_table (&lt;&lt;table,col&gt;&gt;,&lt;&lt;newTable&gt;&gt;)</code>
 * <br />
 * 
 * The column <i>col</i> is moved from <i>table</i> to a new table
 * <i>newTable</i> and it is transformed in a FK pointing at the newly created
 * table
 * 
 * @author Tudor Dobrila
 * 
 */
public class ColToTable extends AbstractPattern {

	/**
	 * Name of the pattern
	 */
	public static final String NAME = "Column to Table";

	/**
	 * Description of the pattern
	 */
	public static final String DESCRIPTION = "Move column to a new table";

	/**
	 * Name of the command associated with the pattern
	 */
	public static final String COMMAND = "col_to_table";

	/**
	 * Integration phase during which the command is applied
	 */
	public static final EnumSet<IntegrationOperation> OPERATION = EnumSet
			.of(IntegrationOperation.CONFORMING);

	/**
	 * Original column string
	 */
	private transient String origColStr;

	/**
	 * Original column (table name, column name)
	 */
	private transient Map.Entry<String, String> origCol;

	/**
	 * Newly created table name
	 */
	private transient String newTableStr;

	/**
	 * Column to table pattern class constructor
	 * 
	 * @param sourceSchema
	 *            Source schema over which the transformation is applied
	 */
	public ColToTable(final Schema sourceSchema) throws NotFoundException, IntegrityException {
		super(sourceSchema);

		pattern = Pattern.compile("^" + COMMAND + "[ ]*\\((" + COL_REGEX + ")" + COMMA_REGEX + "("
				+ TABLE_REGEX + ")\\)$");
	}

	/**
	 * Column to table pattern class constructor
	 * 
	 * @param sourceSchema
	 *            Name of the source schema over which the transformation is
	 *            applied
	 */
	public ColToTable(final String sourceSchema) throws NotFoundException, IntegrityException {
		this(Schema.getSchema(sourceSchema));
	}

	/**
	 * Column to table pattern class constructor
	 * 
	 * @param sourceSchema
	 *            Source schema over which the transformation is applied
	 * @param origColStr
	 *            Original column string
	 * @param newTableStr
	 *            Newly created table name
	 */
	public ColToTable(final Schema sourceSchema, final String origColStr, final String newTableStr)
			throws NotFoundException, IntegrityException {
		this(sourceSchema);
		
		this.origColStr = origColStr;
		this.origCol = parseColName(origColStr);
		this.newTableStr = newTableStr;
	}

	@Override
	protected List<String> getPrimitiveCommands() throws NotFoundException, IntegrityException {
		final List<String> result = new ArrayList<String>();
		final SchemaObject colObj = sourceSchema.getSchemaObject(origColStr);
		final IExtentGenerator gen = new ExtentGenerator(origColStr, origCol);
		StringBuilder cmd = new StringBuilder();

		// Step 1: add new table
		String iql = gen.getExtentsForStep(1).get(0);
		cmd.append("add (");
		cmd.append(Utils.genTableRepresentation(newTableStr));
		cmd.append(", ").append(iql).append(")");
		result.add(cmd.toString());

		// Step 2: add column to the new table
		iql = gen.getExtentsForStep(2).get(0);
		cmd.setLength(0);
		cmd.append("add (");
		Object[] colScheme = colObj.getSchemeDefinition();
		cmd.append(Utils.genColRepresentation(newTableStr, origCol.getValue(), colScheme[2],
				colScheme[3]));
		cmd.append(",").append(iql).append(")");
		result.add(cmd.toString());

		// Step 3: make new column PK in new table
		if (sqlModel.isFeatureInUse(SQLModelDef.PRIMARY_KEY)) {
			Object[] pkArr = new Object[3];
			pkArr[0] = newTableStr + "_pk";
			pkArr[1] = newTableStr;
			pkArr[2] = "<<" + newTableStr + "," + origCol.getValue() + ">>";
			cmd.setLength(0);
			cmd.append("add (");
			cmd.append(Utils.genPKRepresentation(pkArr));
			cmd.append(")");
			result.add(cmd.toString());
		}

		// Step 4: Create FK column in original table
		if (sqlModel.isFeatureInUse(SQLModelDef.FOREIGN_KEY)) {
			List<String> col = new ArrayList<String>();
			col.add(origCol.getValue());
			cmd.setLength(0);
			cmd.append("add (");
			cmd.append(Utils.genFKRepresentation(origCol.getKey(), newTableStr, col));
			cmd.append(")");
			result.add(cmd.toString());
		}

		return result;
	}

	@Override
	protected boolean verify() {
		// TODO Auto-generated method stub
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.model.TransformationPattern#parse(java.lang.String)
	 */
	@Override
	protected void parse(final String trans) {
		final Matcher matcher = pattern.matcher(trans);
		matcher.find();

		origColStr = matcher.group(1);
		origCol = parseColName(origColStr);
		newTableStr = parseTableName(matcher.group(2));
	}

	/**
	 * Extent generator for the <i>Column to table transformation pattern</i>
	 * 
	 * @author Tudor Dobrila
	 * 
	 */
	static class ExtentGenerator implements IExtentGenerator {
		private final transient String origColStr;
		private final transient Entry<String, String> origCol;

		/**
		 * Extent generator class constructor
		 * 
		 * @param origColStr
		 *            Name of the original column in the forward transformation
		 * @param origCol
		 *            (Table,column) pair of the original column
		 */
		public ExtentGenerator(final String origColStr, final Map.Entry<String, String> origCol) {
			this.origColStr = origColStr;
			this.origCol = origCol;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see ro.dta.idbi.patterns.IExtentGenerator#getExtentsForStep(int)
		 */
		@Override
		public List<String> getExtentsForStep(final int step) {
			final List<String> result = new ArrayList<String>();
			StringBuilder iql;

			switch (step) {
			case 1:
				// Step 1: add new table
				iql = new StringBuilder("distinct [ {");
				iql.append(origCol.getValue()).append("} | {y,").append(origCol.getValue());
				iql.append("} <- ").append(origColStr).append("]");
				result.add(iql.toString());
				break;
			case 2:
				// Step 2: add column to the new table
				iql = new StringBuilder("distinct [ {");
				iql.append(origCol.getValue()).append(",").append(origCol.getValue());
				iql.append("} | {y,").append(origCol.getValue());
				iql.append("} <- ").append(origColStr).append("]");
				result.add(iql.toString());
				break;
			default:
			}

			return result;
		}
	}
}
